
#include "commu_ota.h"

#define TAG "ota"
#define OTA_URL "https://api.tenclass.net/xiaozhi/ota/"

static esp_http_client_handle_t client = NULL;
my_ota_t my_ota;
#define OTA_ACTIVATION_BIT ((EventBits_t)0x01)

static char *get_device_id(void);         // 获取设备id
static char *get_client_id(void);         // 获取client_id
static void ota_set_request_header(void); // 设置请求头
static void ota_set_request_body(void);   // 设置请求体
void ota_parse_response(void);            // 解析json

static char *output_buffer; // Buffer to store response of http request from event handler
static int output_len;      // Stores number of bytes read
EventGroupHandle_t eg;
esp_err_t _http_event_handler(esp_http_client_event_t *evt)
{

    switch (evt->event_id)
    {
    case HTTP_EVENT_ERROR:
        ESP_LOGD(TAG, "HTTP_EVENT_ERROR");
        break;
    case HTTP_EVENT_ON_CONNECTED:
        ESP_LOGD(TAG, "HTTP_EVENT_ON_CONNECTED");
        break;
    case HTTP_EVENT_HEADER_SENT:
        ESP_LOGD(TAG, "HTTP_EVENT_HEADER_SENT");
        break;
    case HTTP_EVENT_ON_HEADER:
        ESP_LOGI(TAG, "HTTP_EVENT_ON_HEADER, key=%s, value=%s", evt->header_key, evt->header_value);
        if (strcmp(evt->header_key, "Content-Length") == 0)
        {
            if (output_buffer)
            {
                free(output_buffer);
                output_buffer = NULL;
            }
            output_buffer = malloc(atoi(evt->header_value) + 1);
            output_len = 0;
        }

        break;
    case HTTP_EVENT_ON_DATA:
        ESP_LOGI(TAG, "HTTP_EVENT_ON_DATA, len=%.*s", evt->data_len, (char *)evt->data);
        memcpy(output_buffer + output_len, evt->data, evt->data_len);
        output_len += evt->data_len;

        break;
    case HTTP_EVENT_ON_FINISH:
        ESP_LOGD(TAG, "HTTP_EVENT_ON_FINISH");

        break;
    case HTTP_EVENT_DISCONNECTED:
        ESP_LOGI(TAG, "HTTP_EVENT_DISCONNECTED");

        break;
    case HTTP_EVENT_REDIRECT:
        break;
    }
    return ESP_OK;
}

static void ota_set_request_header(void)
{

    esp_http_client_set_header(client, "Content-Type", "application/json");

    esp_http_client_set_header(client, "User-Agent", "bread-compact-wifi-128x64/1.0.1");

    char *device_id = get_device_id();
    esp_http_client_set_header(client, "Device-ID", device_id);

    char *client_id = get_client_id();
    esp_http_client_set_header(client, "Client-ID", client_id);

    MY_LOGI("device_id: %s", device_id);
    MY_LOGI("client_id: %s", client_id);

    my_ota.client_id = strdup(client_id); // 深度copy
    my_ota.device_id = strdup(device_id);

    free(device_id);
    free(client_id);
}

static void ota_set_request_body(void)
{

    cJSON *root = cJSON_CreateObject();

    cJSON *app = cJSON_CreateObject();

    cJSON_AddStringToObject(app, "version", "1.0.1");
    cJSON_AddStringToObject(app, "elf_sha256", esp_app_get_elf_sha256_str());
    cJSON_AddItemToObject(root, "application", app);

    cJSON *board = cJSON_CreateObject();
    cJSON_AddStringToObject(board, "type", "bread-compact-wifi");
    cJSON_AddStringToObject(board, "name", "bread-compact-wifi-128X64");
    cJSON_AddStringToObject(board, "ssid", "lzcxxx");
    cJSON_AddNumberToObject(board, "rssi", -55);
    cJSON_AddNumberToObject(board, "channel", 1);
    cJSON_AddStringToObject(board, "ip", "192.168.1.1");
    cJSON_AddStringToObject(board, "mac", my_ota.device_id);
    cJSON_AddItemToObject(root, "board", board);

    char *body = cJSON_PrintUnformatted(root);

    MY_LOGI("[ota] post body: %s", body);
    esp_http_client_set_post_field(client, body, strlen(body));

    cJSON_Delete(root);
}

static char *get_device_id(void)
{
    uint8_t mac[6] = {0};
    esp_read_mac(mac, ESP_MAC_WIFI_STA);
    char *mac_str = malloc(18);
    if (mac_str == NULL)
    {
        return NULL;
    }
    sprintf(mac_str, "%02x:%02x:%02x:%02x:%02x:%02x", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
    return mac_str;
}

static char *get_client_id(void)
{
    nvs_handle_t handle;
    nvs_open("ota", NVS_READWRITE, &handle);
    size_t uuid_len = 0;
    nvs_get_str(handle, "uuid", NULL, &uuid_len);

    if (uuid_len > 0)
    {
        char *uuid = malloc(uuid_len + 1);
        nvs_get_str(handle, "uuid", uuid, &uuid_len);
        nvs_close(handle);
        return uuid;
    }
    // 帮我写一个uuid4 生成代码

    unsigned char bytes[16];
    for (int i = 0; i < 16; i++)
    {
        bytes[i] = rand() % 256;
    }

    // 设置 version (第 7 个字节高 4 位为 0100)
    bytes[6] = (bytes[6] & 0x0F) | 0x40;
    // 设置 variant (第 9 个字节高 2 位为 10)
    bytes[8] = (bytes[8] & 0x3F) | 0x80;

    char *uuid = (char *)malloc(37);
    sprintf(uuid,
            "%02x%02x%02x%02x-"
            "%02x%02x-"
            "%02x%02x-"
            "%02x%02x-"
            "%02x%02x%02x%02x%02x%02x",
            bytes[0],
            bytes[1],
            bytes[2],
            bytes[3],
            bytes[4],
            bytes[5],
            bytes[6],
            bytes[7],
            bytes[8],
            bytes[9],
            bytes[10],
            bytes[11],
            bytes[12],
            bytes[13],
            bytes[14],
            bytes[15]);
    // 存储到nvs中
    nvs_set_str(handle, "uuid", uuid);
    nvs_commit(handle);
    nvs_close(handle);
    return uuid;
}

void ota_parse_response(void)
{

    cJSON *root = cJSON_ParseWithLength(output_buffer, output_len);

    if (root == NULL)
    {
        MY_LOGE("响应结果解析失败: %.*s", output_len, output_buffer);
        return;
    }

    cJSON *websocket = cJSON_GetObjectItem(root, "websocket");
    if (websocket != NULL)
    {
        cJSON *url = cJSON_GetObjectItem(websocket, "url");
        if (url != NULL)
        {
            my_ota.ws_url = strdup(url->valuestring);
        }

        cJSON *token = cJSON_GetObjectItem(websocket, "token");
        if (token != NULL)
        {
            my_ota.token = strdup(token->valuestring);
        }
    }

    cJSON *activation = cJSON_GetObjectItem(root, "activation");
    if (activation != NULL)
    {
        cJSON *code = cJSON_GetObjectItem(activation, "code");
        if (code != NULL)
        {
            my_ota.activation_code = strdup(code->valuestring);
        }
    }

    cJSON_Delete(root);
}

void ota_task(void *args)
{
    esp_http_client_config_t config = {
        .url = OTA_URL,
        .event_handler = _http_event_handler,
        .method = HTTP_METHOD_POST,                // post请求
        .crt_bundle_attach = esp_crt_bundle_attach // 验证服务区证书
    };
    client = esp_http_client_init(&config);

    // 1. 设置请求头
    ota_set_request_header();

    // 2. 设置请求体
    ota_set_request_body();

    while (1)
    {

        // 3. 发送请求
        esp_err_t err = esp_http_client_perform(client);

        if (err == ESP_OK)
        {
            ESP_LOGI(TAG, "HTTP POST Status = %d, content_length = %" PRId64,
                     esp_http_client_get_status_code(client),
                     esp_http_client_get_content_length(client));
            ota_parse_response();

            if (my_ota.activation_code != NULL)
            {
                MY_LOGI("请迅速激活设备, 激活码: %s", my_ota.activation_code);
                my_ota.activation_code = NULL;
                // break;
            }
            else
            {
                MY_LOGI("设备已激活");
                xEventGroupSetBits(eg, OTA_ACTIVATION_BIT);
                break;
            }
        }

        vTaskDelay(10000);
    }
    vTaskDelete(NULL);
    free(output_buffer);
    output_buffer = NULL;
    output_len = 0;
}
void commu_ota_send_request(void)
{
    eg = xEventGroupCreate();
    xTaskCreate(&ota_task, "ota_task", 8192, NULL, 5, NULL);
    xEventGroupWaitBits(eg, OTA_ACTIVATION_BIT, pdTRUE, pdTRUE, portMAX_DELAY);
    MY_LOGI("激活完成, 继续执行");
}